/**
 *                     RESTRICTED RIGHTS LEGEND
 *
 *                        BBNT Solutions LLC
 *                        A Verizon Company
 *                        10 Moulton Street
 *                       Cambridge, MA 02138
 *                         (617) 873-3000
 *
 * Copyright BBNT Solutions LLC 2001, 2002 All Rights Reserved
 *
 */
package com.bbn.openmap.geo;

import java.util.Iterator;

/**
 * Iterate along a sequence of Ribbons. A Ribbon is a three Geo set, with a
 * RIGHT, CENTER and LEFT Geo in the Ribbon. The location of the LEFT and RIGHT
 * Geo are perpendicular to the great circle path that the CENTER Geo resides
 * on.
 */
public class RibbonIterator implements Iterator<Ribbon>, Iterable<Ribbon> {

    protected Geo v1;
    protected Geo v2;
    protected double radius;
    protected Geo gc;
    protected Rotation rotator;
    protected Geo point;
    protected double distance;

    /**
     * Return an iterator that returns Ribbons along the great circle between v1
     * and v2. The Ribbon points are radius radians apart, and each Ribbon is
     * 2*radius apart.
     */
    public RibbonIterator(Geo v1, Geo v2, double radius) {
        this(v1, v2, radius, 2.0 * radius);
    }

    /**
     * Return an iterator that returns Ribbons along the great circle between v1
     * and v2. The Ribbon points are radius radians apart, and each Ribbon is
     * rotationIntervalDist apart.
     * 
     * @param v1 from this Geo
     * @param v2 to this Geo
     * @param radius distance away from great circle lines between geos, in
     *        radians.
     * @param rotationIntervalDist interval distance between ribbons, in
     *        radians. You want this to be smaller than the distance between the
     *        geos, obviously.
     */
    public RibbonIterator(Geo v1, Geo v2, double radius, double rotationIntervalDist) {
        this.v1 = v1;
        this.v2 = v2;

        if (v1 == null || v2 == null || v1.equals(v2)) {
            // hasNext should fail
            this.point = v2;
        } else {
            this.distance = v1.distance(v2);
            this.radius = radius;
            this.gc = v1.crossNormalize(v2);
            this.rotator = new Rotation(gc, rotationIntervalDist);
            this.point = v1;
        }
    }

    /**
     * Call after hasNext() returns true.
     * 
     * @return next Ribbon containing points at offset from v1 and v2.
     */
    public Ribbon next() {
        if (!hasNext()) {
            return Ribbon.getEmpty();
        }

        if (point != v2 && v1.distance(point) < distance) {
            Ribbon result = new Ribbon(point, gc, radius);
            point = rotator.rotate(point);
            return result;
        } else {
            point = v2;
            return new Ribbon(point, gc, radius);
        }
    }

    /**
     * Are there more Ribbons in the iterator?
     * 
     * @return true if there are, then call next().
     */
    public boolean hasNext() {
        return point != v2;
    }

    public void remove() {
    }

    public Iterator<Ribbon> iterator() {
        return this;
    }
}
